#ifndef PROXY_TOOL_H
#define PROXY_TOOL_H

#include "types.hpp"
#ifdef  WIN32
	#include <WinSock2.h>
	#pragma comment(lib, "WS2_32.lib")

	typedef SOCKET socket_t;
#else
	#include <unistd.h>
	#include <sys/socket.h>
	#include <sys/types.h>
	#include <netinet/in.h>
	#include <arpa/inet.h>

	typedef int_t socket_t;
#endif

#define PT_SUCCESS  0
#define PT_FAILED  -1

#define PT_NOMEMORY	   -2
#define PT_SOCKETERR   -3
#define PT_CONNECTERR  -4
#define PT_NETWORKERR  -5
#define PT_NOTSUPPORT  -6
#define PT_NOTSETAUTH  -7

#define PT_NOTCONNECT -8
#define PT_NOTAUTH	  -9
/****************************************************************************
 * Abstract base class for proxy_tool
 ****************************************************************************/
#define NAME_LENGTH 255
#define PWD_LENGTH  255
#define ADDR_LENGTH 20
#define BUFF_LENGTH 2048
class CProxyTool {
public:
	/*************************************************************
	 * constructor, initialize system level data structures
	 * for windows platform, it mainly initializes the WS2_32.lib
	 *************************************************************/
	CProxyTool ();

	/*************************************************************
	 * clean up system level data structures
	 * for windows platform, it mainly clean up WS2_32.lib
	 *************************************************************/
	virtual ~CProxyTool();

	/*************************************************************
	 * setup the proxy server information
	 * m_szProxyIp: server ip address
	 * m_sPort: server port
	 *************************************************************/
	virtual int_t SetProxyServer(const char_t* szProxyIp, ushort_t sProxyPort);
	
	/*************************************************************
	 * setup authorisation information
	 * szUser: user name
	 * szPass: user password
	 *************************************************************/
	virtual int_t SetProxyAuth(const char_t* szUser, const char_t* szPass);
	
	/*************************************************************
	 * connect remote server
	 * szServerIp: remote server ip
	 * szServerPort: remote server port
	 *************************************************************/	
	virtual int_t ConnectServer(const char_t* szServerIp, int_t sServerPort);

	/*************************************************************
	 * disconnect from remote server
	 *************************************************************/
	virtual int_t DisconnectServer();
	
	/*************************************************************
	 * return socket
	 *************************************************************/
	virtual int_t GetSocket() {
		return m_sSocket;
	}
	
private:
	/*************************************************************
	 * connect to proxy server, internal method
	 *************************************************************/
	virtual int_t ConnectProxy();
	
	/*************************************************************
	 * authorisation routine, for the user wants to connect to remote
	 * server through the proxy server, so the server info. is passed
	 * into as parameters. after this operation, the socket is in 
	 * connection with remote server. internal method
	 * szServerIp: remote server ip
	 * sServerPort: remote server port
	 *************************************************************/
	virtual int_t Authenticate(const char_t* szServerIp, int_t sServerPort) = 0;

protected:
	char_t  m_szProxyIp[ADDR_LENGTH];
	ushort_t m_sProxyPort;
	
	bool_t  m_bAuth;   // flag indicating if use  auth or not
	bool_t  m_bPass;   // flag indicating if pass auth or not
	char_t  m_szUser[NAME_LENGTH]; // auth info user name
	char_t  m_szPass[PWD_LENGTH];  // auth info user pass

protected:
	socket_t m_sSocket; // socket_t
};

/****************************************************************************
/* base64 encoder used in http auth      
/****************************************************************************/
int_t Base64EncodeBlock(const char_t* szSrc, char_t* szDst, int_t nLength);
int_t Base64Encode(const char_t* szUser, const char_t* szPass, char_t* szResult);

/****************************************************************************
 * proxy_tool for http
 ****************************************************************************/
#define HTTP_NOAUTH 0
#define HTTP_BASIC  1
#define HTTP_DIGEST 2
#define HTTP_NTLM   3

#define SUCCESS_RESPONSE "200"
class CHttpProxyTool : public CProxyTool {
public:
	/****************************************************************************
	* constructor, build http proxy tool 
	****************************************************************************/
	CHttpProxyTool(int_t nAuthMethod = HTTP_NOAUTH);
	/****************************************************************************
	* destructor
	****************************************************************************/
	~CHttpProxyTool();	

private:
	int_t NoAuthMethod(const char_t* szServerIp, int_t sServerPort);
	int_t BasicAuthMethod(const char_t* szServerIp, int_t sServerPort);
	int_t Authenticate(const char_t* szServerIp, int_t sServerPort);

private:
	int_t   m_nAuthMethod; //auth metod
};

/****************************************************************************
 * proxy_tool for socks5
 ****************************************************************************/
#define SOCKS5_VERSION  0x05
#define SOCKS5_RESERVE  0x00

#define SOCKS5_AUTH_VER 0x01
#define SOCKS5_AUTH_NOAUTH   0x00
#define SOCKS5_AUTH_GSSAPI   0x01
#define SOCKS5_AUTH_USERPASS 0x02
#define SOCKS5_AUTH_IANA     0x03
#define SOCKS5_AUTH_PRIVATE  0x80
#define SOCKS5_AUTH_DECLINE  0xFF

#define SOCKS5_ADDR_IPV4   0x01
#define SOCKS5_ADDR_DOMAIN 0x03
#define SOCKS5_ADDR_IPV6   0x04

#define SOCKS5_CMD_CONNECT 0x01
#define SOCKS5_CMD_BIND    0x02
#define SOCKS5_CMD_UDPASSI 0x03

#define SOCKS5_RES_SUCCESS 0x00
#define SOCKS5_RES_COMMON  0x01
#define SOCKS5_RES_DECLINE 0x02
#define SOCKS5_RES_UNREACHNET  0x03
#define SOCKS5_RES_UNREACHHOST 0x04
#define SOCKS5_RES_RESET     0x05
#define SOCKS5_RES_TTL       0x06
#define SOCKS5_RES_INVALCMD  0x07
#define SOCKS5_RES_INVALADDR 0x08
#define SOCKS5_RES_RESERVE   0x09

class CSocks5ProxyTool : public CProxyTool {
public:
	/****************************************************************************
	* constructor, build socks5 proxy tool 
	****************************************************************************/
	CSocks5ProxyTool();
	/****************************************************************************
	* destructor
	****************************************************************************/
	~CSocks5ProxyTool();

private:
	// auth proxy first
	int_t AuthenProxy();
	// setup a communication channel
	int_t SetupChannel(const char_t* szServerIp, int_t sServerPort);
	// framework method calling other methods
	int_t Authenticate(const char_t* szServerIp, int_t sServerPort);
};

/***************************************************************************
* interface to create CProxyTool
* factory method pattern
***************************************************************************/
#define HTTP_PROXY   1
#define SOCKS5_PROXY 2

class CProxyToolFactory {
public:
	CProxyToolFactory() { }
	~CProxyToolFactory() { }

	/***************************************************************************
	* for http   proxy, the @auth parameter should be set
	* for socks5 proxy, the @auth parameter isn't needed
	***************************************************************************/
	 CProxyTool* GetProxyTool(int_t type, int_t auth = HTTP_NOAUTH);
};
#endif